#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import cv2
import numpy as np
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from PIL import Image, ImageTk, ImageDraw, ImageFont
from datetime import datetime
import threading
import time
from ttkthemes import ThemedTk
import traceback

from face_recognition import FaceRecognition
from database import UserDatabase

# 定义配色方案
COLORS = {
    "primary": "#1976d2",       # 主色调（蓝色）
    "secondary": "#f50057",     # 次要色调（粉红色） 
    "background": "#f5f5f5",    # 背景色（浅灰色）
    "card": "#ffffff",          # 卡片背景（白色）
    "text": "#212121",          # 主要文本颜色（深灰色）
    "text_secondary": "#757575",# 次要文本颜色（中灰色）
    "success": "#4caf50",       # 成功色（绿色）
    "warning": "#ff9800",       # 警告色（橙色）
    "error": "#f44336",         # 错误色（红色）
}

# 全局字体设置
FONTS = {
    "title": ("Microsoft YaHei", 16, "bold"),
    "subtitle": ("Microsoft YaHei", 14, "bold"),
    "body": ("Microsoft YaHei", 12),
    "body_small": ("Microsoft YaHei", 10),
    "button": ("Microsoft YaHei", 12, "bold"),
}

# 全局应用程序设置
APP_CONFIG = {
    "title": "桂林电子科技大学数字媒体技术 - 人脸识别签到系统",
    "version": "1.0.0",
    "author": "李雪怡",
    "year": datetime.now().year
}

class ModernFrame(ttk.Frame):
    """现代风格的框架，带有阴影和圆角边框"""
    def __init__(self, parent, title=None, **kwargs):
        if "style" not in kwargs:
            kwargs["style"] = "Card.TFrame"
        super().__init__(parent, **kwargs)
        
        if title:
            title_frame = ttk.Frame(self, style="Card.TFrame")
            title_frame.pack(fill=tk.X, padx=10, pady=(10, 5))
            
            title_label = ttk.Label(title_frame, text=title, font=FONTS["subtitle"], 
                                    foreground=COLORS["primary"])
            title_label.pack(anchor=tk.W)
            
            separator = ttk.Separator(self, orient=tk.HORIZONTAL)
            separator.pack(fill=tk.X, padx=10, pady=5)

class VideoFrame(ModernFrame):
    def __init__(self, master, width=640, height=480, **kwargs):
        """初始化视频显示框架"""
        super(VideoFrame, self).__init__(master, **kwargs)
        
        self.width = width
        self.height = height
        
        # 创建用于显示视频的标签
        self.video_label = ttk.Label(self)
        self.video_label.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
    def show_frame(self, frame):
        """显示视频帧
        
        Args:
            frame: OpenCV图像帧
        """
        if frame is None:
            return
            
        # 转换图像格式以在tkinter中显示
        rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        pil_image = Image.fromarray(rgb_frame)
        pil_image = pil_image.resize((self.width, self.height), Image.LANCZOS)
        
        self.photo = ImageTk.PhotoImage(image=pil_image)
        self.video_label.config(image=self.photo)

class ModernProgressbar(ttk.Progressbar):
    """现代风格的进度条"""
    def __init__(self, parent, **kwargs):
        super().__init__(parent, **kwargs)

class RegisterDialog(tk.Toplevel):
    def __init__(self, parent, title="注册新用户"):
        """注册用户对话框"""
        super().__init__(parent)
        self.title(title)
        self.geometry("400x400")
        self.resizable(False, False)
        self.configure(bg=COLORS["background"])
        
        # 设置模态
        self.transient(parent)
        self.grab_set()
        
        self.user_info = None
        self.create_widgets()
        
        # 窗口居中
        self.update_idletasks()
        width = self.winfo_width()
        height = self.winfo_height()
        x = (self.winfo_screenwidth() // 2) - (width // 2)
        y = (self.winfo_screenheight() // 2) - (height // 2)
        self.geometry(f"+{x}+{y}")
        
    def create_widgets(self):
        """创建对话框主体"""
        # 主框架
        main_frame = ModernFrame(self)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
        
        # 标题
        title_label = ttk.Label(main_frame, text="添加新用户", font=FONTS["title"],
                                foreground=COLORS["primary"])
        title_label.pack(pady=(10, 20))
        
        # 输入框架
        form_frame = ttk.Frame(main_frame)
        form_frame.pack(fill=tk.X, padx=20, pady=10)
        
        # 姓名
        name_frame = ttk.Frame(form_frame)
        name_frame.pack(fill=tk.X, pady=5)
        ttk.Label(name_frame, text="姓名:", font=FONTS["body"]).pack(side=tk.LEFT)
        self.name_entry = ttk.Entry(name_frame, font=FONTS["body"], width=25)
        self.name_entry.pack(side=tk.RIGHT)
        
        # 学号/工号
        id_frame = ttk.Frame(form_frame)
        id_frame.pack(fill=tk.X, pady=5)
        ttk.Label(id_frame, text="学号/工号:", font=FONTS["body"]).pack(side=tk.LEFT)
        self.student_id_entry = ttk.Entry(id_frame, font=FONTS["body"], width=25)
        self.student_id_entry.pack(side=tk.RIGHT)
        
        # 部门/班级
        dept_frame = ttk.Frame(form_frame)
        dept_frame.pack(fill=tk.X, pady=5)
        ttk.Label(dept_frame, text="部门/班级:", font=FONTS["body"]).pack(side=tk.LEFT)
        self.department_entry = ttk.Entry(dept_frame, font=FONTS["body"], width=25)
        self.department_entry.pack(side=tk.RIGHT)
        
        # 按钮框架
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X, padx=20, pady=20)
        
        # 取消按钮
        cancel_btn = ttk.Button(button_frame, text="取消",
                                command=self.destroy)
        cancel_btn.pack(side=tk.LEFT, padx=10)
        
        # 确定按钮
        ok_btn = ttk.Button(button_frame, text="确定",
                          command=self.apply)
        ok_btn.pack(side=tk.RIGHT, padx=10)
        
        # 设置初始焦点
        self.name_entry.focus_set()
    
    def apply(self):
        """确定按钮被点击时调用"""
        # 验证输入
        name = self.name_entry.get().strip()
        student_id = self.student_id_entry.get().strip()
        department = self.department_entry.get().strip()
        
        if not name:
            messagebox.showerror("错误", "姓名不能为空！")
            self.name_entry.focus_set()
            return
        
        self.user_info = {
            'name': name,
            'student_id': student_id,
            'department': department
        }
        self.destroy()

class AttendanceDetailDialog(tk.Toplevel):
    def __init__(self, parent, name, time, image_path, data_path):
        """签到详情对话框"""
        super().__init__(parent)
        self.title("签到详情")
        self.geometry("500x600")
        self.resizable(False, False)
        self.configure(bg=COLORS["background"])
        
        # 设置模态
        self.transient(parent)
        self.grab_set()
        
        self.name = name
        self.time = time
        self.image_path = image_path
        self.data_path = data_path
        
        self.create_widgets()
        
        # 窗口居中
        self.update_idletasks()
        width = self.winfo_width()
        height = self.winfo_height()
        x = (self.winfo_screenwidth() // 2) - (width // 2)
        y = (self.winfo_screenheight() // 2) - (height // 2)
        self.geometry(f"+{x}+{y}")
        
    def create_widgets(self):
        """创建对话框主体"""
        # 主框架
        main_frame = ModernFrame(self)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
        
        # 标题
        title_label = ttk.Label(main_frame, text="签到详情", font=FONTS["title"],
                                foreground=COLORS["primary"])
        title_label.pack(pady=(10, 20))
        
        # 信息框架
        info_frame = ttk.Frame(main_frame, style="Card.TFrame")
        info_frame.pack(fill=tk.X, padx=10, pady=10)
        
        # 姓名信息
        name_frame = ttk.Frame(info_frame, style="Card.TFrame")
        name_frame.pack(fill=tk.X, padx=15, pady=10)
        ttk.Label(name_frame, text="姓名:", font=FONTS["subtitle"], style="Card.TLabel").pack(side=tk.LEFT)
        ttk.Label(name_frame, text=self.name, font=FONTS["body"], 
                 foreground=COLORS["primary"], style="Card.TLabel").pack(side=tk.LEFT, padx=(10, 0))
        
        # 时间信息
        time_frame = ttk.Frame(info_frame, style="Card.TFrame")
        time_frame.pack(fill=tk.X, padx=15, pady=5)
        ttk.Label(time_frame, text="签到时间:", font=FONTS["subtitle"], style="Card.TLabel").pack(side=tk.LEFT)
        ttk.Label(time_frame, text=self.time, font=FONTS["body"], 
                 foreground=COLORS["success"], style="Card.TLabel").pack(side=tk.LEFT, padx=(10, 0))
        
        # 图像框架
        image_frame = ModernFrame(main_frame, title="签到照片")
        image_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 创建图像显示标签
        self.image_label = ttk.Label(image_frame, style="Card.TLabel")
        self.image_label.pack(fill=tk.BOTH, expand=True, padx=15, pady=15)
        
        # 加载并显示图像
        self.load_image()
        
        # 按钮框架
        button_frame = ttk.Frame(main_frame, style="Card.TFrame")
        button_frame.pack(fill=tk.X, padx=10, pady=20)
        
        # 保存图像按钮
        save_btn = ttk.Button(button_frame, text="保存图像", command=self.save_image)
        save_btn.pack(side=tk.LEFT, padx=10)
        
        # 关闭按钮
        close_btn = ttk.Button(button_frame, text="关闭", command=self.destroy)
        close_btn.pack(side=tk.RIGHT, padx=10)
        
    def load_image(self):
        """加载并显示签到图像"""
        try:
            if self.image_path and self.image_path.strip():
                # 构建完整的图像路径
                full_image_path = os.path.join(self.data_path, 'attendance', self.image_path)
                
                if os.path.exists(full_image_path):
                    # 使用PIL加载图像
                    pil_image = Image.open(full_image_path)
                    
                    # 调整图像大小以适应显示
                    display_size = (300, 200)
                    pil_image = pil_image.resize(display_size, Image.LANCZOS)
                    
                    # 转换为tkinter可显示的格式
                    self.photo = ImageTk.PhotoImage(pil_image)
                    self.image_label.config(image=self.photo)
                    
                    # 显示图像信息
                    self.image_label.config(text="")  # 清除文本
                else:
                    self.image_label.config(text="图像文件不存在\n" + full_image_path, 
                                          foreground=COLORS["error"])
            else:
                self.image_label.config(text="无签到照片", foreground=COLORS["text_secondary"])
                
        except Exception as e:
            print(f"加载签到图像失败: {str(e)}")
            self.image_label.config(text=f"加载图像失败\n{str(e)}", 
                                  foreground=COLORS["error"])
    
    def save_image(self):
        """保存签到图像到用户选择的位置"""
        if not self.image_path or not self.image_path.strip():
            messagebox.showinfo("提示", "没有可保存的图像")
            return
            
        try:
            # 构建源文件路径
            source_path = os.path.join(self.data_path, 'attendance', self.image_path)
            
            if not os.path.exists(source_path):
                messagebox.showerror("错误", "源图像文件不存在")
                return
            
            # 选择保存位置
            file_extension = os.path.splitext(self.image_path)[1]
            default_filename = f"{self.name}_签到照片_{self.time.replace(':', '-')}{file_extension}"
            
            save_path = filedialog.asksaveasfilename(
                defaultextension=file_extension,
                filetypes=[("图像文件", "*.jpg *.jpeg *.png"), ("所有文件", "*.*")],
                initialfile=default_filename
            )
            
            if save_path:
                # 复制文件
                import shutil
                shutil.copy2(source_path, save_path)
                messagebox.showinfo("成功", f"图像已保存到:\n{save_path}")
                
        except Exception as e:
            messagebox.showerror("错误", f"保存图像失败:\n{str(e)}")

class MainApp(ThemedTk):
    def __init__(self):
        """初始化主应用程序"""
        super(MainApp, self).__init__(theme="arc")  # 使用arc主题
        
        # 设置窗口基本属性
        self.title(APP_CONFIG["title"])
        self.geometry("1000x900")
        self.minsize(1000, 900)
        
        # 自定义样式
        self.setup_styles()
        
        # 初始化组件
        self.face_recognition = FaceRecognition()
        self.user_db = UserDatabase()
        
        # 视频捕获
        self.cap = None
        self.is_camera_running = False
        self.update_id = None
        
        # 采集人脸时使用的缓冲区
        self.face_frames = []
        self.capture_count = 0
        self.is_registering = False
        self.recognition_mode = False
        
        # 创建界面
        self.setup_ui()
        
        # 确保进度条框架已创建但未显示
        self.progress_frame = ttk.Frame(self, style="Card.TFrame")
        self.progress_label = ttk.Label(self.progress_frame, text="人脸采集进度:", style="Status.TLabel")
        self.progress_bar = ModernProgressbar(self.progress_frame, orient=tk.HORIZONTAL, 
                                          length=200, mode='determinate')
        
        # 关闭窗口时处理
        self.protocol("WM_DELETE_WINDOW", self.on_closing)
        
        # 加载用户和签到记录
        self.load_user_list()
        self.load_attendance_records()
        
        # 在应用启动时，显示默认图像
        self.after(500, self.show_default_image)
    
    def setup_styles(self):
        """设置自定义样式"""
        style = ttk.Style()
        
        # 基础颜色和字体设置
        style.configure(".", font=FONTS["body"])
        
        # 框架样式
        style.configure("TFrame", background=COLORS["background"])
        style.configure("Card.TFrame", background=COLORS["card"])
        
        # 标签样式
        style.configure("TLabel", background=COLORS["background"], font=FONTS["body"])
        style.configure("Card.TLabel", background=COLORS["card"], font=FONTS["body"])
        style.configure("Title.TLabel", font=FONTS["title"], foreground=COLORS["primary"])
        style.configure("Subtitle.TLabel", font=FONTS["subtitle"], foreground=COLORS["primary"])
        style.configure("Status.TLabel", font=FONTS["body"], background=COLORS["card"])
        style.configure("Success.TLabel", foreground=COLORS["success"])
        style.configure("Warning.TLabel", foreground=COLORS["warning"])
        style.configure("Error.TLabel", foreground=COLORS["error"])
        
        # 按钮样式 - 关键修复：确保字体颜色是黑色或深色
        style.configure("TButton", font=FONTS["button"], padding=6, foreground=COLORS["text"])
        
        # 自定义按钮样式 - 确保文本是黑色
        style.configure("Primary.TButton", foreground=COLORS["text"])
        style.configure("Secondary.TButton", foreground=COLORS["text"])
        style.configure("Success.TButton", foreground=COLORS["text"])
        style.configure("Warning.TButton", foreground=COLORS["text"])
        style.configure("Error.TButton", foreground=COLORS["text"])
        
        # 树状视图
        style.configure("Treeview", 
                        background=COLORS["card"],
                        fieldbackground=COLORS["card"],
                        foreground=COLORS["text"],
                        font=FONTS["body_small"])
        style.configure("Treeview.Heading", 
                        font=FONTS["body"],
                        background=COLORS["primary"],
                        foreground=COLORS["text"])  # 将表头文字颜色从白色改为黑色
                        
        # 进度条样式
        style.configure("Horizontal.TProgressbar", 
                        background=COLORS["primary"],
                        troughcolor=COLORS["background"])
    
    def setup_ui(self):
        """设置用户界面"""
        self.configure(background=COLORS["background"])
        
        # 顶部标题栏
        self.create_header()
        
        # 主框架
        main_frame = ttk.Frame(self, style="TFrame")
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=(0, 10))
        
        # 标签页
        self.notebook = ttk.Notebook(main_frame)
        self.notebook.pack(fill=tk.BOTH, expand=True)
        
        # 添加标签页切换事件绑定
        self.notebook.bind("<<NotebookTabChanged>>", self.on_tab_changed)
        
        # 签到标签页
        self.attendance_frame = ttk.Frame(self.notebook, style="TFrame")
        self.notebook.add(self.attendance_frame, text="人脸签到")
        
        # 用户管理标签页
        self.user_frame = ttk.Frame(self.notebook, style="TFrame")
        self.notebook.add(self.user_frame, text="用户管理")
        
        # 模型训练标签页
        self.train_frame = ttk.Frame(self.notebook, style="TFrame")
        self.notebook.add(self.train_frame, text="模型训练")
        
        # 设置签到标签页
        self.setup_attendance_tab()
        
        # 设置用户管理标签页
        self.setup_user_tab()
        
        # 设置模型训练标签页
        self.setup_train_tab()
        
        # 底部状态栏
        self.create_footer()
    
    def create_header(self):
        """创建顶部标题栏"""
        header_frame = ttk.Frame(self, style="TFrame")
        header_frame.pack(fill=tk.X, padx=10, pady=10)
        
        # 标题
        title_label = ttk.Label(header_frame, text=APP_CONFIG["title"], 
                               style="Title.TLabel")
        title_label.pack(side=tk.LEFT)
        
        # 当前日期时间
        self.time_label = ttk.Label(header_frame, text="", style="TLabel")
        self.time_label.pack(side=tk.RIGHT)
        self.update_time()
    
    def update_time(self):
        """更新时间显示"""
        current_time = datetime.now().strftime("%Y年%m月%d日 %H:%M:%S")
        self.time_label.config(text=current_time)
        self.after(1000, self.update_time)
    
    def create_footer(self):
        """创建底部状态栏"""
        footer_frame = ttk.Frame(self, style="TFrame")
        footer_frame.pack(fill=tk.X, padx=10, pady=(0, 5))
        
        # 版本信息
        version_label = ttk.Label(footer_frame, text=f"版本: {APP_CONFIG['version']}", style="TLabel")
        version_label.pack(side=tk.LEFT)
        
        # 作者信息
        author_label = ttk.Label(footer_frame, text=f"© {APP_CONFIG['year']} {APP_CONFIG['author']}", style="TLabel")
        author_label.pack(side=tk.RIGHT)
    
    def setup_attendance_tab(self):
        """设置签到标签页"""
        # 左右分隔
        left_frame = ttk.Frame(self.attendance_frame, style="TFrame")
        left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        right_frame = ttk.Frame(self.attendance_frame, style="TFrame")
        right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, padx=5, pady=5)
        
        # 设置右侧框架的宽度
        right_frame.configure(width=350)
        
        # 左侧视频显示区域
        video_container = ModernFrame(left_frame, title="实时摄像头")
        video_container.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        self.video_frame = VideoFrame(video_container, width=640, height=480)
        self.video_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 控制按钮面板
        control_frame = ModernFrame(left_frame, title="控制面板")
        control_frame.pack(fill=tk.X, pady=10, padx=5)
        
        button_frame = ttk.Frame(control_frame, style="Card.TFrame")
        button_frame.pack(fill=tk.X, padx=20, pady=15)
        
        # 开启摄像头按钮
        self.start_button = ttk.Button(button_frame, text="开启摄像头", 
                                     command=self.toggle_camera)
        self.start_button.pack(side=tk.LEFT, padx=10)
        
        # 注册人脸按钮
        self.register_button = ttk.Button(button_frame, text="注册人脸", 
                                        command=self.start_face_registration)
        self.register_button.pack(side=tk.LEFT, padx=10)
        self.register_button.config(state=tk.DISABLED)
        
        # 识别并签到按钮
        self.recognize_button = ttk.Button(button_frame, text="识别并签到", 
                                         command=self.start_recognition)
        self.recognize_button.pack(side=tk.LEFT, padx=10)
        self.recognize_button.config(state=tk.DISABLED)
        
        # 右侧状态和签到记录
        status_frame = ModernFrame(right_frame, title="识别状态")
        status_frame.pack(fill=tk.X, padx=5, pady=5)
        
        status_content = ttk.Frame(status_frame, style="Card.TFrame")
        status_content.pack(fill=tk.X, padx=15, pady=15)
        
        # 状态信息
        self.status_label = ttk.Label(status_content, text="状态: 等待开始", 
                                    style="Status.TLabel", foreground=COLORS["text"])
        self.status_label.pack(anchor=tk.W, padx=5, pady=5)
        
        self.name_label = ttk.Label(status_content, text="姓名: --", 
                                  style="Status.TLabel", foreground=COLORS["text"])
        self.name_label.pack(anchor=tk.W, padx=5, pady=5)
        
        self.confidence_label = ttk.Label(status_content, text="置信度: --", 
                                        style="Status.TLabel", foreground=COLORS["text"])
        self.confidence_label.pack(anchor=tk.W, padx=5, pady=5)
        
        # 人脸采集进度
        self.progress_frame = ttk.Frame(status_content, style="Card.TFrame")
        self.progress_label = ttk.Label(self.progress_frame, text="人脸采集进度:", style="Status.TLabel")
        self.progress_bar = ModernProgressbar(self.progress_frame, orient=tk.HORIZONTAL, 
                                            length=200, mode='determinate')
        
        # 今日签到记录
        record_frame = ModernFrame(right_frame, title="今日签到记录")
        record_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 创建表格容器
        tree_container = ttk.Frame(record_frame, style="Card.TFrame")
        tree_container.pack(fill=tk.BOTH, expand=True, padx=15, pady=15)
        
        # 创建签到记录表格
        self.record_tree = ttk.Treeview(tree_container, columns=("name", "time", "image"), show="headings", height=10)
        self.record_tree.heading("name", text="姓名")
        self.record_tree.heading("time", text="时间")
        self.record_tree.heading("image", text="图像")
        self.record_tree.column("name", width=100, anchor=tk.CENTER)
        self.record_tree.column("time", width=120, anchor=tk.CENTER)
        self.record_tree.column("image", width=0, stretch=False)  # 隐藏图像列
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(tree_container, orient=tk.VERTICAL, command=self.record_tree.yview)
        self.record_tree.configure(yscrollcommand=scrollbar.set)
        
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.record_tree.pack(fill=tk.BOTH, expand=True)
        
        # 绑定双击事件
        self.record_tree.bind("<Double-1>", self.show_attendance_detail)
        
        # 添加统计信息
        stats_frame = ttk.Frame(record_frame, style="Card.TFrame")
        stats_frame.pack(fill=tk.X, padx=15, pady=(0, 5))
        
        self.record_count_label = ttk.Label(stats_frame, text="今日签到人次: 0", 
                                          style="Card.TLabel", foreground=COLORS["primary"])
        self.record_count_label.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 添加按钮框架
        record_buttons_frame = ttk.Frame(record_frame, style="Card.TFrame")
        record_buttons_frame.pack(fill=tk.X, padx=15, pady=(0, 15))
        
        # 导出按钮
        self.export_button = ttk.Button(record_buttons_frame, text="导出记录", 
                                       command=self.export_attendance_records)
        self.export_button.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 清空按钮
        self.clear_button = ttk.Button(record_buttons_frame, text="清空记录", 
                                      command=self.clear_attendance_records)
        self.clear_button.pack(side=tk.LEFT, padx=5, pady=5)

    def update_frame(self):
        """更新视频帧"""
        if not self.is_camera_running or not self.cap:
            return
        
        try:
            ret, frame = self.cap.read()
            if not ret or frame is None:
                self.status_label.config(text="状态: 无法读取视频帧", foreground=COLORS["error"])
                self.update_id = self.after(100, self.update_frame)  # 稍后再尝试
                return
            
            # 水平翻转，使其更符合自拍习惯
            frame = cv2.flip(frame, 1)
            
            # 如果是在注册过程中，收集人脸样本
            if self.is_registering:
                # 捕获10帧用于训练
                if len(self.face_frames) < 10:
                    try:
                        faces, _ = self.face_recognition.detect_faces(frame)
                        
                        if len(faces) == 1:  # 确保只有一个人脸
                            self.face_frames.append(frame.copy())
                            self.capture_count += 1
                            
                            # 更新进度条
                            self.progress_bar['value'] = (self.capture_count / 10) * 100
                            self.progress_label.config(text=f"人脸采集进度: {self.capture_count}/10")
                            
                            # 在图像上绘制人脸框
                            for (x, y, w, h) in faces:
                                cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
                                # 使用函数在OpenCV图像上添加中文文本
                                frame = self.face_recognition.cv2_add_chinese_text(
                                    frame, 
                                    f"采集中: {self.capture_count}/10", 
                                    (x, y-30), 
                                    self.face_recognition.font_path,
                                    24, 
                                    (0, 255, 0)
                                )
                        
                        if self.capture_count >= 10:
                            # 完成采集
                            self.status_label.config(text="状态: 人脸采集完成，正在注册...", 
                                                   foreground=COLORS["success"])
                            self.after(500, self.finish_registration)
                    except Exception as e:
                        print(f"人脸检测出错: {str(e)}")
                
            # 如果在识别模式（按下了"识别并签到"按钮）
            elif self.recognition_mode:
                try:
                    success, name, confidence = self.face_recognition.recognize_face(frame)
                    
                    faces, _ = self.face_recognition.detect_faces(frame)
                    face_location = None
                    
                    for (x, y, w, h) in faces:
                        # 记录识别出的人脸位置
                        if success:
                            face_location = (x, y, w, h)
                            
                        # 画出人脸框
                        if success:
                            # 识别成功，绿色框
                            cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
                            # 使用函数在OpenCV图像上添加中文文本
                            frame = self.face_recognition.cv2_add_chinese_text(
                                frame, 
                                f"{name} ({confidence:.1f})", 
                                (x, y-30), 
                                self.face_recognition.font_path,
                                24, 
                                (0, 255, 0)
                            )
                        else:
                            # 识别失败，红色框
                            cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 0, 255), 2)
                            # 使用函数在OpenCV图像上添加中文文本
                            frame = self.face_recognition.cv2_add_chinese_text(
                                frame, 
                                "未识别", 
                                (x, y-30), 
                                self.face_recognition.font_path,
                                24, 
                                (0, 0, 255)
                            )
                    
                    if success:
                        self.name_label.config(text=f"姓名: {name}", foreground=COLORS["success"])
                        self.confidence_label.config(text=f"置信度: {confidence:.1f}", 
                                                  foreground=COLORS["success"])
                        
                        # 添加签到记录，同时传递人脸图像和位置
                        if self.face_recognition.record_attendance(name, frame.copy(), face_location):
                            self.status_label.config(text=f"状态: {name} 签到成功!", 
                                                  foreground=COLORS["success"])
                            
                            # 弹出签到成功确认框
                            messagebox.showinfo("签到成功", f"{name} 已成功签到!")
                            
                            # 刷新签到记录
                            self.load_attendance_records()
                            
                            # 切换回普通模式
                            self.recognition_mode = False
                            self.recognize_button.config(text="识别并签到")
                        else:
                            messagebox.showerror("错误", "数据库添加签到记录失败")
                    else:
                        if faces:
                            self.name_label.config(text="姓名: 未识别", foreground=COLORS["error"])
                            self.confidence_label.config(text="置信度: --", foreground=COLORS["error"])
                            self.status_label.config(text="状态: 无法识别人脸，请尝试调整位置", 
                                                  foreground=COLORS["warning"])
                except Exception as e:
                    print(f"人脸识别出错: {str(e)}")
            else:
                # 正常模式，也显示识别结果
                try:
                    faces, _ = self.face_recognition.detect_faces(frame)
                    
                    # 尝试识别人脸，即使不在识别模式下
                    if len(faces) > 0:
                        try:
                            success, name, confidence = self.face_recognition.recognize_face(frame)
                            
                            for (x, y, w, h) in faces:
                                if success:
                                    # 识别成功，蓝色框
                                    cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)
                                    # 使用函数在OpenCV图像上添加中文文本
                                    frame = self.face_recognition.cv2_add_chinese_text(
                                        frame, 
                                        f"{name} ({confidence:.1f})", 
                                        (x, y-30), 
                                        self.face_recognition.font_path,
                                        24, 
                                        (255, 0, 0)
                                    )
                                    # 更新状态标签
                                    self.name_label.config(text=f"姓名: {name}", foreground=COLORS["primary"])
                                    self.confidence_label.config(text=f"置信度: {confidence:.1f}", 
                                                              foreground=COLORS["primary"])
                                    self.status_label.config(text='状态: 请按"识别并签到"按钮进行签到', 
                                                           foreground=COLORS["primary"])
                                else:
                                    # 识别失败，黄色框
                                    cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 255), 2)
                                    # 使用函数在OpenCV图像上添加中文文本
                                    frame = self.face_recognition.cv2_add_chinese_text(
                                        frame, 
                                        "未注册用户", 
                                        (x, y-30), 
                                        self.face_recognition.font_path,
                                        24, 
                                        (0, 255, 255)
                                    )
                                    # 更新状态标签
                                    self.name_label.config(text="姓名: 未注册用户", foreground=COLORS["warning"])
                                    self.confidence_label.config(text="置信度: --", foreground=COLORS["warning"])
                                    self.status_label.config(text="状态: 请先注册人脸", 
                                                           foreground=COLORS["warning"])
                        except Exception as e:
                            print(f"非识别模式下人脸识别出错: {str(e)}")
                            # 在出错时仍然显示人脸框
                            for (x, y, w, h) in faces:
                                cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 165, 0), 2)  # 橙色边框表示识别过程有错误
                except Exception as e:
                    print(f"人脸检测/识别出错: {str(e)}")
            
            # 显示处理后的帧
            try:
                self.video_frame.show_frame(frame)
            except Exception as e:
                print(f"显示图像出错: {str(e)}")
            
            # 循环更新
            self.update_id = self.after(15, self.update_frame)
        except Exception as e:
            print(f"视频帧处理出错: {str(e)}")
            self.update_id = self.after(100, self.update_frame)  # 稍后再尝试

    # 添加导出签到记录功能
    def export_attendance_records(self):
        """导出签到记录到CSV文件"""
        # 获取当前日期
        today = datetime.now().strftime("%Y-%m-%d")
        
        # 签到记录文件路径
        attendance_file = os.path.join(self.face_recognition.data_path, 'attendance', f"{today}.csv")
        
        if not os.path.exists(attendance_file):
            messagebox.showinfo("提示", "今天还没有签到记录")
            return
        
        # 选择保存路径
        save_path = filedialog.asksaveasfilename(
            defaultextension=".csv",
            filetypes=[("CSV 文件", "*.csv"), ("所有文件", "*.*")],
            initialfile=f"签到记录_{today}.csv"
        )
        
        if not save_path:
            return  # 用户取消了保存
        
        try:
            # 复制文件
            with open(attendance_file, 'r', encoding='utf-8') as src:
                with open(save_path, 'w', encoding='utf-8') as dst:
                    dst.write(src.read())
            
            messagebox.showinfo("成功", f"签到记录已导出到 {save_path}")
        except Exception as e:
            messagebox.showerror("错误", f"导出失败: {str(e)}")

    # 添加清空签到记录功能
    def clear_attendance_records(self):
        """清空当天签到记录"""
        if messagebox.askyesno("确认", "确定要清空今天的所有签到记录吗？此操作不可撤销！"):
            today = datetime.now().strftime("%Y-%m-%d")
            
            if self.face_recognition.clear_attendance(today):
                messagebox.showinfo("成功", "签到记录已清空")
                # 刷新显示
                self.load_attendance_records()
            else:
                messagebox.showerror("错误", "清空签到记录失败")

    def toggle_camera(self):
        """切换摄像头状态"""
        if self.is_camera_running:
            # 停止摄像头
            self.is_camera_running = False
            if self.update_id:
                self.after_cancel(self.update_id)
                self.update_id = None
            
            if self.cap and self.cap.isOpened():
                self.cap.release()
                self.cap = None
            
            self.start_button.config(text="开启摄像头")
            self.register_button.config(state=tk.DISABLED)
            self.recognize_button.config(state=tk.DISABLED)
            
            self.status_label.config(text="状态: 摄像头已关闭")
            
            # 移除进度条（如果存在）
            if self.progress_frame.winfo_ismapped():
                self.progress_frame.pack_forget()
                
            # 显示默认图像
            self.show_default_image()
        else:
            # 开启摄像头
            try:
                # 输出调试信息
                print("尝试打开摄像头...")
                
                # 尝试使用不同的摄像头索引
                camera_opened = False
                
                try:
                    # 先尝试DirectShow
                    self.cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
                    print(f"DirectShow摄像头状态: {'已打开' if self.cap.isOpened() else '未打开'}")
                    if self.cap.isOpened():
                        camera_opened = True
                    else:
                        # 释放资源
                        self.cap.release()
                except Exception as e:
                    print(f"DirectShow摄像头打开失败: {str(e)}")
                
                if not camera_opened:
                    try:
                        # 尝试默认后端
                        self.cap = cv2.VideoCapture(0)
                        print(f"默认摄像头状态: {'已打开' if self.cap.isOpened() else '未打开'}")
                        if self.cap.isOpened():
                            camera_opened = True
                        else:
                            # 释放资源
                            self.cap.release()
                    except Exception as e:
                        print(f"默认摄像头打开失败: {str(e)}")
                
                if not camera_opened:
                    try:
                        # 尝试第二个摄像头
                        self.cap = cv2.VideoCapture(1)
                        print(f"摄像头1状态: {'已打开' if self.cap.isOpened() else '未打开'}")
                        if self.cap.isOpened():
                            camera_opened = True
                        else:
                            # 释放资源
                            self.cap.release()
                    except Exception as e:
                        print(f"摄像头1打开失败: {str(e)}")
                
                if not camera_opened:
                    # 所有尝试都失败
                    error_msg = "无法打开摄像头。请检查:\n"
                    error_msg += "1. 摄像头是否已正确连接\n"
                    error_msg += "2. 其他程序是否正在使用摄像头\n"
                    error_msg += "3. 摄像头驱动是否已正确安装"
                    
                    print(error_msg)
                    messagebox.showerror("摄像头错误", error_msg)
                    return
                
                # 设置摄像头属性
                self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
                self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
                
                # 读取一帧测试
                ret, test_frame = self.cap.read()
                if not ret or test_frame is None:
                    print("摄像头无法读取帧")
                    messagebox.showerror("错误", "摄像头已打开但无法读取视频帧")
                    self.cap.release()
                    return
                
                print("摄像头已成功打开并可以读取帧")
                
                self.is_camera_running = True
                self.start_button.config(text="关闭摄像头")
                self.register_button.config(state=tk.NORMAL)
                self.recognize_button.config(state=tk.NORMAL)
                
                self.status_label.config(text="状态: 摄像头已开启")
                self.update_frame()
            except Exception as e:
                error_msg = f"摄像头启动失败: {str(e)}\n\n"
                error_msg += "请检查摄像头连接并确保没有其他程序正在使用它。"
                messagebox.showerror("错误", error_msg)
                print(f"摄像头启动详细错误: {traceback.format_exc()}")
                
                # 显示默认图像并设置状态
                self.show_default_image()
                self.status_label.config(text="状态: 摄像头打开失败", foreground=COLORS["error"])
    
    def finish_registration(self):
        """完成人脸注册"""
        if not self.is_registering or not self.current_user_info:
            return
        
        # 注册人脸
        if self.face_recognition.register_face(self.current_user_info['name'], self.face_frames):
            # 在数据库中添加用户信息
            user_info = self.current_user_info.copy()
            user_info['register_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            
            # 添加到数据库
            success = self.user_db.add_user(user_info)
            
            if success:
                messagebox.showinfo("成功", f"{user_info['name']} 注册成功！")
                self.status_label.config(text=f"状态: {user_info['name']} 注册成功", 
                                       foreground=COLORS["success"])
                
                # 刷新用户列表
                self.load_user_list()
            else:
                messagebox.showerror("错误", "数据库添加用户失败")
                self.status_label.config(text="状态: 数据库添加用户失败", 
                                       foreground=COLORS["error"])
        else:
            messagebox.showerror("错误", "人脸注册失败，请确保采集的图像质量良好")
            self.status_label.config(text="状态: 人脸注册失败", foreground=COLORS["error"])
        
        # 重置状态
        self.is_registering = False
        self.current_user_info = None
        self.face_frames = []
        self.capture_count = 0
        
        # 重置UI
        self.register_button.config(state=tk.NORMAL)
        self.recognize_button.config(state=tk.NORMAL)
        
        # 隐藏进度条
        self.progress_frame.pack_forget()
    
    def start_recognition(self):
        """开始人脸识别"""
        if not self.is_camera_running:
            messagebox.showerror("错误", "请先开启摄像头")
            return
            
        if self.recognition_mode:
            # 关闭识别模式
            self.recognition_mode = False
            self.recognize_button.config(text="识别并签到")
            self.status_label.config(text="状态: 摄像头已开启")
            self.name_label.config(text="姓名: --")
            self.confidence_label.config(text="置信度: --")
        else:
            # 开启识别模式
            self.recognition_mode = True
            self.is_registering = False
            self.recognize_button.config(text="停止识别")
            self.status_label.config(text="状态: 正在识别...")
    
    def load_user_list(self):
        """加载用户列表"""
        # 清空现有列表
        for item in self.user_tree.get_children():
            self.user_tree.delete(item)
        
        # 获取所有用户
        users = self.user_db.get_all_users()
        
        # 添加到表格
        for user in users:
            self.user_tree.insert("", tk.END, values=(
                user["id"], 
                user["name"], 
                user["student_id"],
                user["department"]
            ))
        
        # 更新统计信息
        self.user_count_label.config(text=f"总用户数: {len(users)}")
    
    def load_attendance_records(self):
        """加载今日签到记录"""
        # 清空表格
        for item in self.record_tree.get_children():
            self.record_tree.delete(item)
        
        # 获取今天的日期
        today = datetime.now().strftime("%Y-%m-%d")
        
        # 签到记录文件路径
        attendance_file = os.path.join(self.face_recognition.data_path, 'attendance', f"{today}.csv")
        
        if os.path.exists(attendance_file):
            with open(attendance_file, 'r', encoding='utf-8') as f:
                next(f)  # 跳过标题行
                for line in f:
                    if line.strip():
                        parts = line.strip().split(',')
                        if len(parts) >= 2:
                            name = parts[0]
                            time = parts[1]
                            # 图像路径可能存在
                            image_path = parts[2] if len(parts) > 2 else ""
                            
                            # 添加到树形视图，同时保存图像路径
                            self.record_tree.insert("", tk.END, values=(name, time, image_path))
        
        # 更新签到记录数
        count = len(self.record_tree.get_children())
        self.record_count_label.config(text=f"今日签到人次: {count}")

    def delete_user(self):
        """删除用户"""
        selected_item = self.user_tree.selection()
        if not selected_item:
            messagebox.showinfo("提示", "请先选择要删除的用户")
            return
            
        user_values = self.user_tree.item(selected_item, "values")
        user_id = user_values[0]
        user_name = user_values[1]
        
        # 确认删除，明确指出将同时删除人脸数据和相关签到照片
        if messagebox.askyesno("确认删除", f"确定要删除用户 {user_name} 吗？\n\n此操作将同时删除：\n- 用户基本信息\n- 人脸识别数据\n- 该用户的所有签到照片\n\n此操作不可撤销！"):
            # 从数据库删除用户
            if self.user_db.delete_user(user_id):
                # 同时从人脸识别模型中删除
                self.face_recognition.delete_user(user_name)
                
                # 从UI移除
                self.user_tree.delete(selected_item)
                messagebox.showinfo("成功", f"用户 {user_name} 及相关数据已成功删除")
                
                # 更新统计信息
                count = len(self.user_tree.get_children())
                self.user_count_label.config(text=f"总用户数: {count}")
            else:
                messagebox.showerror("错误", f"删除用户 {user_name} 失败")
    
    def on_closing(self):
        """窗口关闭时调用"""
        # 停止所有运行的摄像头
        self.is_camera_running = False
        self.train_camera_running = False
        
        # 取消所有待处理的更新任务
        if self.update_id:
            self.after_cancel(self.update_id)
            self.update_id = None
        
        # 释放摄像头资源
        if self.cap and self.cap.isOpened():
            self.cap.release()
            self.cap = None
        
        # 关闭窗口
        self.destroy()

    def setup_user_tab(self):
        """设置用户管理标签页"""
        # 创建用户管理容器
        user_container = ModernFrame(self.user_frame, title="用户列表")
        user_container.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 创建用户列表内容容器
        content_frame = ttk.Frame(user_container, style="Card.TFrame")
        content_frame.pack(fill=tk.BOTH, expand=True, padx=15, pady=15)
        
        # 创建工具栏
        toolbar = ttk.Frame(content_frame, style="Card.TFrame")
        toolbar.pack(fill=tk.X, padx=5, pady=5)
        
        # 搜索框
        search_frame = ttk.Frame(toolbar, style="Card.TFrame")
        search_frame.pack(side=tk.LEFT, fill=tk.X, expand=True)
        
        ttk.Label(search_frame, text="搜索:", style="Card.TLabel").pack(side=tk.LEFT, padx=(0, 5))
        self.search_var = tk.StringVar()
        search_entry = ttk.Entry(search_frame, textvariable=self.search_var, width=30)
        search_entry.pack(side=tk.LEFT, padx=5)
        self.search_var.trace_add("write", self.filter_users)
        
        # 按钮区
        button_frame = ttk.Frame(toolbar, style="Card.TFrame")
        button_frame.pack(side=tk.RIGHT, fill=tk.Y)
        
        self.refresh_button = ttk.Button(button_frame, text="刷新", 
                                       command=self.load_user_list)
        self.refresh_button.pack(side=tk.LEFT, padx=5)
        
        self.delete_button = ttk.Button(button_frame, text="删除用户", 
                                      command=self.delete_user)
        self.delete_button.pack(side=tk.LEFT, padx=5)
        
        # 用户列表区域
        table_frame = ttk.Frame(content_frame, style="Card.TFrame")
        table_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 用户列表
        self.user_tree = ttk.Treeview(
            table_frame, 
            columns=("id", "name", "student_id", "department"), 
            show="headings", 
            height=15
        )
        
        self.user_tree.heading("id", text="ID")
        self.user_tree.heading("name", text="姓名")
        self.user_tree.heading("student_id", text="学号/工号")
        self.user_tree.heading("department", text="部门/班级")
        
        self.user_tree.column("id", width=50, anchor=tk.CENTER)
        self.user_tree.column("name", width=100, anchor=tk.CENTER)
        self.user_tree.column("student_id", width=120, anchor=tk.CENTER)
        self.user_tree.column("department", width=150, anchor=tk.CENTER)
        
        # 添加滚动条
        y_scrollbar = ttk.Scrollbar(table_frame, orient=tk.VERTICAL, command=self.user_tree.yview)
        x_scrollbar = ttk.Scrollbar(table_frame, orient=tk.HORIZONTAL, command=self.user_tree.xview)
        self.user_tree.configure(yscrollcommand=y_scrollbar.set, xscrollcommand=x_scrollbar.set)
        
        y_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        x_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)
        self.user_tree.pack(fill=tk.BOTH, expand=True)
        
        # 底部统计信息
        stats_frame = ttk.Frame(content_frame, style="Card.TFrame")
        stats_frame.pack(fill=tk.X, padx=5, pady=5)
        
        self.user_count_label = ttk.Label(stats_frame, text="总用户数: 0", style="Card.TLabel")
        self.user_count_label.pack(side=tk.LEFT)
        
        # 右键菜单
        self.context_menu = tk.Menu(self.user_tree, tearoff=0)
        self.context_menu.add_command(label="删除", command=self.delete_user)
        self.context_menu.add_command(label="查看详情", command=self.view_user_details)
        
        self.user_tree.bind("<Button-3>", self.show_context_menu)

    def filter_users(self, *args):
        """根据搜索条件筛选用户"""
        search_text = self.search_var.get().lower()
        if not search_text:
            self.load_user_list()  # 如果搜索框为空，加载所有用户
            return
        
        # 清空现有列表
        for item in self.user_tree.get_children():
            self.user_tree.delete(item)
        
        # 获取所有用户
        users = self.user_db.get_all_users()
        
        # 筛选并显示符合条件的用户
        filtered_count = 0
        for user in users:
            if (search_text in user["name"].lower() or 
                search_text in str(user["student_id"]).lower() or 
                search_text in str(user["department"]).lower()):
                
                self.user_tree.insert("", tk.END, values=(
                    user["id"], 
                    user["name"], 
                    user["student_id"],
                    user["department"]
                ))
                filtered_count += 1
                
        # 更新统计信息
        self.user_count_label.config(text=f"筛选用户数: {filtered_count}")

    def show_context_menu(self, event):
        """显示右键菜单"""
        # 确保右键点击的是一个项目
        item = self.user_tree.identify_row(event.y)
        if not item:
            return
        
        # 选中被右键点击的项目
        self.user_tree.selection_set(item)
        
        # 显示上下文菜单
        self.context_menu.post(event.x_root, event.y_root)

    def view_user_details(self):
        """查看用户详情"""
        selected_item = self.user_tree.selection()
        if not selected_item:
            messagebox.showinfo("提示", "请先选择一个用户")
            return
        
        user_id = self.user_tree.item(selected_item, "values")[0]
        user = self.user_db.get_user_by_id(user_id)
        
        if user:
            # 创建详情对话框
            details = "\n".join([
                f"ID: {user['id']}",
                f"姓名: {user['name']}",
                f"学号/工号: {user['student_id']}",
                f"部门/班级: {user['department']}",
                f"注册时间: {user.get('register_time', '未知')}"
            ])
            
            messagebox.showinfo("用户详情", details)
        else:
            messagebox.showerror("错误", "找不到该用户信息")

    # 重新添加start_face_registration方法
    def start_face_registration(self):
        """开始人脸注册流程"""
        if not self.is_camera_running:
            messagebox.showerror("错误", "请先开启摄像头")
            return
        
        # 显示注册对话框
        dialog = RegisterDialog(self)
        self.wait_window(dialog)
        
        if dialog.user_info:
            # 清空之前的人脸帧
            self.face_frames = []
            self.capture_count = 0
            
            # 开始注册流程
            self.is_registering = True
            self.recognition_mode = False
            
            # 更新UI
            self.register_button.config(state=tk.DISABLED)
            self.recognize_button.config(state=tk.DISABLED)
            self.status_label.config(text=f"状态: 正在注册 {dialog.user_info['name']} 的人脸...", 
                                    foreground=COLORS["primary"])
            
            # 显示进度条
            self.progress_frame.pack(fill=tk.X, padx=5, pady=5)
            self.progress_label.pack(anchor=tk.W)
            self.progress_bar.pack(fill=tk.X, padx=5, pady=5)
            self.progress_bar['value'] = 0
            
            # 存储用户信息
            self.current_user_info = dialog.user_info

    # 添加show_default_image方法（修改版，使用中文字体）
    def show_default_image(self):
        """显示默认图像"""
        # 创建一个带文字的黑色图像
        img = np.zeros((480, 640, 3), dtype=np.uint8)
        img.fill(50)  # 深灰色背景
        
        # 使用cv2_add_chinese_text函数添加中文
        try:
            img = self.face_recognition.cv2_add_chinese_text(
                img,
                "摄像头已关闭",
                (220, 220),
                self.face_recognition.font_path,
                36,
                (200, 200, 200)
            )
        except Exception as e:
            print(f"添加中文文本失败: {str(e)}")
            # 如果添加中文失败，使用英文替代
            cv2.putText(img, "Camera Off", (220, 240), 
                       cv2.FONT_HERSHEY_SIMPLEX, 1, (200, 200, 200), 2)
        
        # 显示图像
        self.video_frame.show_frame(img)

    def show_attendance_detail(self, event):
        """显示签到详情"""
        # 获取选中的条目
        item = self.record_tree.selection()
        if not item:
            return
        
        # 获取详情
        values = self.record_tree.item(item, "values")
        if len(values) >= 3:
            name = values[0]
            time = values[1]
            image_path = values[2]
            
            # 显示详情对话框
            AttendanceDetailDialog(self, name, time, image_path, self.face_recognition.data_path)

    def setup_train_tab(self):
        """设置模型训练标签页"""
        # 创建模型训练容器
        train_container = ModernFrame(self.train_frame, title="人脸模型训练")
        train_container.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 创建模型训练内容容器
        content_frame = ttk.Frame(train_container, style="Card.TFrame")
        content_frame.pack(fill=tk.BOTH, expand=True, padx=15, pady=15)
        
        # 左右分隔
        left_frame = ttk.Frame(content_frame, style="Card.TFrame")
        left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        right_frame = ttk.Frame(content_frame, style="Card.TFrame")
        right_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=5, pady=5)
        
        # 左侧 - 摄像头视频显示区域
        video_container = ModernFrame(left_frame, title="实时摄像头")
        video_container.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        self.train_video_frame = VideoFrame(video_container, width=640, height=480)
        self.train_video_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 摄像头控制按钮
        camera_control_frame = ttk.Frame(video_container, style="Card.TFrame")
        camera_control_frame.pack(fill=tk.X, padx=10, pady=10)
        
        self.train_camera_button = ttk.Button(camera_control_frame, text="开启摄像头", 
                                            command=self.toggle_train_camera)
        self.train_camera_button.pack(side=tk.LEFT, padx=10)
        
        # 标记模式开关
        self.marking_mode_var = tk.BooleanVar(value=False)
        self.marking_mode_check = ttk.Checkbutton(camera_control_frame, text="进入标记模式", 
                                               variable=self.marking_mode_var,
                                               command=self.toggle_marking_mode)
        self.marking_mode_check.pack(side=tk.LEFT, padx=10)
        
        # 标记按钮框架
        self.mark_buttons_frame = ttk.Frame(video_container, style="Card.TFrame")
        self.mark_buttons_frame.pack(fill=tk.X, padx=10, pady=10)
        self.mark_buttons_frame.pack_forget()  # 初始隐藏，进入标记模式时显示
        
        # 标记为人脸/非人脸按钮
        self.mark_face_button = ttk.Button(self.mark_buttons_frame, text="标记为人脸", 
                                         command=lambda: self.mark_current_detection(True))
        self.mark_face_button.pack(side=tk.LEFT, padx=10)
        
        self.mark_nonface_button = ttk.Button(self.mark_buttons_frame, text="标记为非人脸", 
                                           command=lambda: self.mark_current_detection(False))
        self.mark_nonface_button.pack(side=tk.LEFT, padx=10)
        
        # 下一个/跳过按钮
        self.next_detection_button = ttk.Button(self.mark_buttons_frame, text="跳过当前", 
                                             command=self.next_detection)
        self.next_detection_button.pack(side=tk.LEFT, padx=10)
        
        # 右侧 - 控制面板
        control_container = ModernFrame(right_frame, title="训练控制")
        control_container.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        control_content = ttk.Frame(control_container, style="Card.TFrame")
        control_content.pack(fill=tk.BOTH, expand=True, padx=15, pady=15)
        
        # 状态标签
        self.train_status_label = ttk.Label(control_content, text="状态: 等待开启摄像头", 
                                        style="Card.TLabel")
        self.train_status_label.pack(fill=tk.X, padx=5, pady=5)
        
        # 当前选中框信息
        self.current_selection_label = ttk.Label(control_content, text="当前选中: 无", 
                                            style="Card.TLabel")
        self.current_selection_label.pack(fill=tk.X, padx=5, pady=5)
        
        # 统计信息
        self.stats_frame = ttk.Frame(control_content, style="Card.TFrame")
        self.stats_frame.pack(fill=tk.X, padx=5, pady=10)
        
        ttk.Label(self.stats_frame, text="已采集样本:", style="Card.TLabel").grid(row=0, column=0, sticky=tk.W, pady=2)
        self.total_samples_label = ttk.Label(self.stats_frame, text="0", style="Card.TLabel")
        self.total_samples_label.grid(row=0, column=1, sticky=tk.W, pady=2)
        
        ttk.Label(self.stats_frame, text="人脸样本:", style="Card.TLabel").grid(row=1, column=0, sticky=tk.W, pady=2)
        self.face_count_label = ttk.Label(self.stats_frame, text="0", style="Card.TLabel")
        self.face_count_label.grid(row=1, column=1, sticky=tk.W, pady=2)
        
        ttk.Label(self.stats_frame, text="非人脸样本:", style="Card.TLabel").grid(row=2, column=0, sticky=tk.W, pady=2)
        self.nonface_count_label = ttk.Label(self.stats_frame, text="0", style="Card.TLabel")
        self.nonface_count_label.grid(row=2, column=1, sticky=tk.W, pady=2)
        
        # 采样控制
        sampling_frame = ttk.Frame(control_content, style="Card.TFrame")
        sampling_frame.pack(fill=tk.X, padx=5, pady=10)
        
        ttk.Label(sampling_frame, text="采样间隔:", style="Card.TLabel").grid(row=0, column=0, sticky=tk.W, pady=2)
        ttk.Label(sampling_frame, text="10秒/次", style="Card.TLabel").grid(row=0, column=1, sticky=tk.W, pady=2)
        
        ttk.Label(sampling_frame, text="目标样本数:", style="Card.TLabel").grid(row=1, column=0, sticky=tk.W, pady=2)
        self.target_samples = ttk.Spinbox(sampling_frame, from_=10, to=1000, width=5)
        self.target_samples.set(100)  # 默认目标采集100个样本
        self.target_samples.grid(row=1, column=1, sticky=tk.W, pady=2)
        
        # 自动采样开关
        self.auto_sampling_var = tk.BooleanVar(value=False)
        self.auto_sampling_check = ttk.Checkbutton(control_content, text="启用自动采样", 
                                                variable=self.auto_sampling_var)
        self.auto_sampling_check.pack(anchor=tk.W, padx=5, pady=5)
        
        # 训练进度
        progress_frame = ttk.Frame(control_content, style="Card.TFrame")
        progress_frame.pack(fill=tk.X, padx=5, pady=10)
        
        self.train_progress_label = ttk.Label(progress_frame, text="训练进度:", style="Card.TLabel")
        self.train_progress_label.pack(anchor=tk.W, pady=2)
        
        self.train_progress_bar = ModernProgressbar(progress_frame, orient=tk.HORIZONTAL, 
                                                  length=200, mode='determinate')
        self.train_progress_bar.pack(fill=tk.X, pady=5)
        self.train_progress_bar['value'] = 0
        
        # 训练按钮
        self.train_button = ttk.Button(control_content, text="开始训练模型", 
                                      command=self.train_face_model)
        self.train_button.pack(fill=tk.X, padx=5, pady=5)
        self.train_button.config(state=tk.DISABLED)
        
        # 初始化训练数据
        self.train_camera_running = False
        self.is_marking_mode = False
        self.face_samples = []  # 存储人脸样本
        self.nonface_samples = []  # 存储非人脸样本
        self.current_detections = []  # 当前帧检测到的所有可能人脸
        self.current_detection_index = -1  # 当前选中的检测框索引
        self.frame_count = 0  # 帧计数器，用于控制采样频率
        
    def toggle_train_camera(self):
        """切换训练标签页摄像头状态"""
        if self.train_camera_running:
            # 如果主摄像头已打开，共用同一个视频流
            if self.is_camera_running and self.cap and self.cap.isOpened():
                self.train_camera_running = False
                self.train_camera_button.config(text="开启摄像头")
                self.train_status_label.config(text="状态: 摄像头已关闭")
                self.auto_sampling_var.set(False)
                self.marking_mode_var.set(False)
                self.toggle_marking_mode()
                
                # 停止在训练标签页显示视频
                self.show_default_image_in_train()
                
                # 保持主页面摄像头开启状态
                # 不关闭self.cap
            else:
                # 关闭摄像头
                self.train_camera_running = False
                if self.cap and self.cap.isOpened():
                    # 只有当主页面没有使用摄像头时才释放
                    if not self.is_camera_running:
                        self.cap.release()
                        self.cap = None
                
                self.train_camera_button.config(text="开启摄像头")
                self.train_status_label.config(text="状态: 摄像头已关闭")
                self.auto_sampling_var.set(False)
                self.marking_mode_var.set(False)
                self.toggle_marking_mode()
                
                # 显示默认图像
                self.show_default_image_in_train()
        else:
            # 检查主摄像头是否已开启
            if self.is_camera_running and self.cap and self.cap.isOpened():
                # 直接使用主摄像头
                self.train_camera_running = True
                self.train_camera_button.config(text="关闭摄像头")
                self.train_status_label.config(text="状态: 摄像头已开启")
                self.train_button.config(state=tk.NORMAL)
                
                # 开始在训练页面更新帧
                self.update_train_frame()
            else:
                # 尝试打开摄像头
                try:
                    # 先尝试DirectShow
                    self.cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
                    if not self.cap.isOpened():
                        self.cap.release()
                        # 尝试默认后端
                        self.cap = cv2.VideoCapture(0)
                        
                    if not self.cap.isOpened():
                        messagebox.showerror("错误", "无法打开摄像头，请检查连接")
                        return
                    
                    # 设置分辨率
                    self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
                    self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
                    
                    self.train_camera_running = True
                    self.train_camera_button.config(text="关闭摄像头")
                    self.train_status_label.config(text="状态: 摄像头已开启")
                    self.train_button.config(state=tk.NORMAL)
                    
                    # 开始更新帧
                    self.update_train_frame()
                except Exception as e:
                    messagebox.showerror("错误", f"打开摄像头失败: {str(e)}")
    
    def show_default_image_in_train(self):
        """在训练标签页显示默认图像"""
        # 创建一个带文字的黑色图像
        img = np.zeros((480, 640, 3), dtype=np.uint8)
        img.fill(50)  # 深灰色背景
        
        try:
            img = self.face_recognition.cv2_add_chinese_text(
                img,
                "摄像头已关闭",
                (220, 220),
                self.face_recognition.font_path,
                36,
                (200, 200, 200)
            )
        except Exception as e:
            print(f"添加中文文本失败: {str(e)}")
            # 如果添加中文失败，使用英文替代
            cv2.putText(img, "Camera Off", (220, 240), 
                       cv2.FONT_HERSHEY_SIMPLEX, 1, (200, 200, 200), 2)
                    
                    # 显示图像
        self.train_video_frame.show_frame(img)
    
    def toggle_marking_mode(self):
        """切换标记模式"""
        self.is_marking_mode = self.marking_mode_var.get()
        
        if self.is_marking_mode:
            self.mark_buttons_frame.pack(fill=tk.X, padx=10, pady=10)
            self.train_status_label.config(text="状态: 标记模式 - 选择检测框进行标记")
            # 禁用自动采样
            self.auto_sampling_var.set(False)
            self.auto_sampling_check.config(state=tk.DISABLED)
        else:
            self.mark_buttons_frame.pack_forget()
            self.train_status_label.config(text="状态: 摄像头已开启")
            # 重新启用自动采样
            self.auto_sampling_check.config(state=tk.NORMAL)
            # 重置当前选择
            self.current_detection_index = -1
            self.current_selection_label.config(text="当前选中: 无")
    
    def update_train_frame(self):
        """更新训练标签页的视频帧"""
        if not self.train_camera_running or not self.cap or not self.cap.isOpened():
            return
        
        try:
            # 只在notebook当前显示的是训练页面时更新画面
            if self.notebook.index("current") == 2:  # 训练页面索引为2
                ret, frame = self.cap.read()
                if not ret or frame is None:
                    self.train_status_label.config(text="状态: 无法读取视频帧", foreground=COLORS["error"])
                    self.after(100, self.update_train_frame)  # 稍后再尝试
                    return
                
                # 水平翻转，使其更符合自拍习惯
                frame = cv2.flip(frame, 1)
                
                # 复制原始帧，用于显示
                display_frame = frame.copy()
                
                # 检测人脸
                faces, _ = self.face_recognition.detect_faces(frame)
                self.current_detections = faces  # 保存当前检测结果
                
                # 在标记模式下显示所有检测框并高亮当前选中的框
                if self.is_marking_mode:
                    # 绘制所有检测到的人脸框
                    for i, (x, y, w, h) in enumerate(faces):
                        # 判断是否是当前选中的框
                        if i == self.current_detection_index:
                            # 当前选中的框用红色加粗显示
                            cv2.rectangle(display_frame, (x, y), (x+w, y+h), (0, 0, 255), 3)
                            # 更新选中标签
                            self.current_selection_label.config(text=f"当前选中: 检测框 {i+1}/{len(faces)}")
                        else:
                            # 其他框用白色细线显示
                            cv2.rectangle(display_frame, (x, y), (x+w, y+h), (255, 255, 255), 1)
                            
                    # 如果没有选中任何框但有检测结果，自动选择第一个
                    if self.current_detection_index == -1 and len(faces) > 0:
                        self.current_detection_index = 0
                        self.current_selection_label.config(text=f"当前选中: 检测框 1/{len(faces)}")
                    
                    # 如果当前没有检测到人脸，重置选择
                    if len(faces) == 0:
                        self.current_detection_index = -1
                        self.current_selection_label.config(text="当前选中: 无")
                    
                    # 防止索引越界
                    if self.current_detection_index >= len(faces):
                        self.current_detection_index = -1 if len(faces) == 0 else 0
                        
                else:
                    # 非标记模式下，只显示检测框
                    for (x, y, w, h) in faces:
                        cv2.rectangle(display_frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
                    
                    # 自动采样模式
                    if self.auto_sampling_var.get() and len(faces) > 0:
                        # 使用基于时间的间隔而非帧数
                        current_time = time.time()
                        
                        # 如果是第一次调用或者已经初始化了last_sample_time
                        if not hasattr(self, 'last_sample_time'):
                            self.last_sample_time = current_time
                        
                        # 检查是否已经过了10秒
                        if current_time - self.last_sample_time >= 10.0:  # 10秒取样一次
                            self.last_sample_time = current_time  # 更新上次取样时间
                            
                            # 随机选择一个检测框作为人脸样本
                            sample_index = np.random.randint(0, len(faces))
                            x, y, w, h = faces[sample_index]
                            face_roi = frame[y:y+h, x:x+w]
                            
                            # 保存为人脸样本
                            self.add_face_sample(face_roi)
                            
                            # 在图像上显示取样成功的提示
                            cv2.putText(display_frame, "已取样!", (10, 30), 
                                       cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
                            
                            # 检查是否达到目标样本数
                            target = int(self.target_samples.get())
                            total_samples = len(self.face_samples) + len(self.nonface_samples)
                            
                            if total_samples >= target:
                                self.auto_sampling_var.set(False)
                                self.train_status_label.config(text=f"状态: 已达到目标样本数 {target}")
                
                # 显示处理后的帧
                self.train_video_frame.show_frame(display_frame)
        
        except Exception as e:
            print(f"训练视频帧处理出错: {str(e)}")
        
        # 循环更新（只有在训练页面仍然可见且摄像头运行时）
        if self.train_camera_running and self.cap and self.cap.isOpened():
            self.after(15, self.update_train_frame)
    
    def mark_current_detection(self, is_face):
        """标记当前选中的检测框为人脸或非人脸"""
        if not self.is_marking_mode or self.current_detection_index == -1:
            return
        
        if self.current_detection_index >= len(self.current_detections):
            messagebox.showinfo("提示", "没有选中有效的检测框")
            return
        
        # 获取当前选中的检测框
        x, y, w, h = self.current_detections[self.current_detection_index]
        
        # 从当前帧中提取roi
        ret, frame = self.cap.read()
        if not ret or frame is None:
            messagebox.showerror("错误", "无法读取视频帧")
            return
        
        # 水平翻转，与显示一致
        frame = cv2.flip(frame, 1)
        
        # 提取区域
        face_roi = frame[y:y+h, x:x+w]
        
        # 根据标记类型添加样本
        if is_face:
            self.add_face_sample(face_roi)
        else:
            self.add_nonface_sample(face_roi)
        
        # 自动切换到下一个检测框
        self.next_detection()
    
    def next_detection(self):
        """切换到下一个检测框"""
        if not self.is_marking_mode or len(self.current_detections) == 0:
            return
        
        if self.current_detection_index + 1 < len(self.current_detections):
            self.current_detection_index += 1
        else:
            self.current_detection_index = 0
            
        self.current_selection_label.config(
            text=f"当前选中: 检测框 {self.current_detection_index+1}/{len(self.current_detections)}")
    
    def add_face_sample(self, sample):
        """添加人脸样本"""
        if sample is None or sample.size == 0:
            return
        
        # 转换为灰度图
        if len(sample.shape) == 3:
            sample = cv2.cvtColor(sample, cv2.COLOR_BGR2GRAY)
            
        # 调整大小
        sample = cv2.resize(sample, (100, 100))
        
        # 图像增强
        sample = cv2.equalizeHist(sample)
        clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
        sample = clahe.apply(sample)
        
        # 添加到样本列表
        self.face_samples.append(sample)
        
        # 更新统计信息
        self.face_count_label.config(text=str(len(self.face_samples)))
        self.total_samples_label.config(text=str(len(self.face_samples) + len(self.nonface_samples)))
        
        # 如果样本数达到一定数量，启用训练按钮
        if len(self.face_samples) >= 5 and len(self.nonface_samples) >= 5:
            self.train_button.config(state=tk.NORMAL)
        
        # 更新状态
        self.train_status_label.config(text=f"状态: 已添加人脸样本，共 {len(self.face_samples)} 个")
    
    def add_nonface_sample(self, sample):
        """添加非人脸样本"""
        if sample is None or sample.size == 0:
            return
        
        # 转换为灰度图
        if len(sample.shape) == 3:
            sample = cv2.cvtColor(sample, cv2.COLOR_BGR2GRAY)
            
        # 调整大小
        sample = cv2.resize(sample, (100, 100))
        
        # 图像增强
        sample = cv2.equalizeHist(sample)
        clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
        sample = clahe.apply(sample)
        
        # 添加到样本列表
        self.nonface_samples.append(sample)
        
        # 更新统计信息
        self.nonface_count_label.config(text=str(len(self.nonface_samples)))
        self.total_samples_label.config(text=str(len(self.face_samples) + len(self.nonface_samples)))
        
        # 如果样本数达到一定数量，启用训练按钮
        if len(self.face_samples) >= 5 and len(self.nonface_samples) >= 5:
            self.train_button.config(state=tk.NORMAL)
        
        # 更新状态
        self.train_status_label.config(text=f"状态: 已添加非人脸样本，共 {len(self.nonface_samples)} 个")
    
    def train_face_model(self):
        """训练人脸识别模型"""
        # 检查是否有足够的标记数据
        if len(self.face_samples) < 5 or len(self.nonface_samples) < 5:
            messagebox.showwarning("警告", "训练数据不足，请至少标记5个人脸和5个非人脸样本")
            return
        
        # 确认开始训练
        if not messagebox.askyesno("确认", "开始训练模型将使用标记的样本更新当前人脸识别模型。继续？"):
            return
        
        # 开始训练
        self.train_progress_label.config(text="正在准备训练数据...")
        self.train_progress_bar['value'] = 10
        self.train_button.config(state=tk.DISABLED)
        
        # 创建单独的线程进行训练，避免冻结UI
        threading.Thread(target=self._train_model_with_samples_thread, daemon=True).start()
    
    def _train_model_with_samples_thread(self):
        """在后台线程中使用采集的样本进行模型训练"""
        try:
            # 更新进度
            self.update_train_progress("正在处理样本数据...", 20)
            
            # 准备标签数据
            face_labels = [1] * len(self.face_samples)  # 1表示人脸
            nonface_labels = [2] * len(self.nonface_samples)  # 2表示非人脸
            
            # 合并样本和标签
            all_samples = self.face_samples + self.nonface_samples
            all_labels = face_labels + nonface_labels
            
            # 更新进度
            self.update_train_progress("正在训练模型...", 50)
            
            # 训练模型
            if len(all_samples) > 0:
                # 尝试训练模型
                try:
                    # 创建新的识别器
                    recognizer = cv2.face.LBPHFaceRecognizer_create()
                    recognizer.train(all_samples, np.array(all_labels))
                    
                    # 保存模型
                    model_save_path = os.path.join(self.face_recognition.data_path, 'face_classifier.yml')
                    recognizer.write(model_save_path)
                    
                    # 更新当前模型
                    self.face_recognition.recognizer = recognizer
                    
                    # 完成训练
                    self.update_train_progress("模型训练完成!", 100)
                    self.after(0, lambda: messagebox.showinfo("成功", "人脸模型训练成功!"))
                except Exception as e:
                    self.update_train_progress(f"训练出错: {str(e)}", 0)
                    self.after(0, lambda: messagebox.showerror("错误", f"训练过程出错: {str(e)}"))
            else:
                self.update_train_progress("没有有效的训练样本", 0)
                self.after(0, lambda: messagebox.showerror("错误", "没有有效的训练样本"))
                
        except Exception as e:
            # 在UI线程中显示错误
            self.after(0, lambda: messagebox.showerror("错误", f"训练过程出错: {str(e)}"))
        finally:
            # 重新启用训练按钮
            self.after(0, lambda: self.train_button.config(state=tk.NORMAL))
    
    def update_train_progress(self, message, value):
        """更新训练进度"""
        # 这个方法可能会从后台线程调用，需要确保在UI线程中更新
        self.after(0, lambda: self.train_progress_label.config(text=message))
        self.after(0, lambda: self.train_progress_bar.config(value=value))

    def on_tab_changed(self, event):
        """处理标签页切换事件"""
        current_tab = self.notebook.index("current")
        
        # 如果切换到签到页面
        if current_tab == 0:  # 签到页面的索引为0
            # 如果摄像头已开启，重新初始化人脸识别状态
            if self.is_camera_running and self.cap and self.cap.isOpened():
                # 更新状态标签
                self.status_label.config(text="状态: 摄像头已开启")
                # 重置识别状态但保持摄像头开启
                self.recognition_mode = False
                self.recognize_button.config(text="识别并签到")
                # 确保人脸识别模型是最新的
                self.face_recognition.load_face_recognizer()
                
        # 如果切换到训练页面
        elif current_tab == 2:  # 训练页面的索引为2
            # 如果主摄像头已开启，同步训练页面的摄像头状态
            if self.is_camera_running and self.cap and self.cap.isOpened():
                self.train_camera_running = True
                self.train_camera_button.config(text="关闭摄像头")
                self.train_status_label.config(text="状态: 摄像头已开启")
                self.train_button.config(state=tk.NORMAL)

def main():
    """主程序入口"""
    app = MainApp()
    app.protocol("WM_DELETE_WINDOW", app.on_closing)
    app.mainloop()

if __name__ == "__main__":
    main()